package com.koko.controller;

import cn.hutool.core.util.RandomUtil;
import com.koko.constant.JwtClaimsConstant;
import com.koko.constant.MessageConstant;
import com.koko.constant.RequestMapKeyConstant;
import com.koko.exception.PasswordErrorException;
import com.koko.exception.VerityCodeException;
import com.koko.feign.ThirdPartyFeignService;
import com.koko.properties.JwtAdminProperties;
import com.koko.properties.JwtUserProperties;
import com.koko.result.Result;
import com.koko.service.UserService;
import com.koko.user.dto.EmailLoginBodyDTO;
import com.koko.user.dto.LoginBodyDTO;
import com.koko.user.dto.RecoverPasswordDTO;
import com.koko.user.dto.RegisterDTO;
import com.koko.user.vo.ImageVerifyVO;
import com.koko.user.vo.UserVO;
import com.koko.utils.Base64Util;
import com.koko.utils.JwtUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;
import com.google.code.kaptcha.Producer;

import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Slf4j
@RestController
@RequestMapping("/user")
public class LoginController {
    @Value("${koko.redis.prefix.emailVerifyCode}")
    private String emailVerifyCodeRedisPrefix;
    @Value("${koko.redis.prefix.imageVerifyCode}")
    private String imageVerifyCodeRedisPrefix;
    @Value("${koko.redis.timeout}")
    private Integer timeout;
    @Value("${koko.path.image}")
    private String basePath;

    @Autowired
    private Producer producer;
    @Autowired
    private ThirdPartyFeignService thirdPartyFeignService;
    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private UserService userService;
    @Autowired
    private JwtUserProperties jwtUserProperties;

    /**
     * 获取图形验证码
     *
     * @throws IOException
     */
    @GetMapping("/getverifycode")
    public Result<ImageVerifyVO> getVerifyCode() {
        // 1. 生成验证码
        String text = producer.createText();
        log.info("图片验证码为：" + text);
        // 2. 生成文字对应的图片验证码
        BufferedImage image = producer.createImage(text);
        // 3. 将图形验证码转化为base64字符串
        String base64 = Base64Util.convertBufferedImageToBase64(image);
        // 4. 生成token
        String token = UUID.randomUUID().toString();
        // 5. 生成验证码对应的token  以token为key  验证码为value存在redis中
        redisTemplate.opsForValue().set(imageVerifyCodeRedisPrefix + token, text, timeout, TimeUnit.MILLISECONDS);
        // 6. 封装数据并返回
        ImageVerifyVO imageVerifyVO = ImageVerifyVO.builder()
                .image(base64)
                .token(token)
                .build();
        return Result.success(imageVerifyVO);
    }

    /**
     * 获取邮箱验证码
     *
     * @param email
     * @return
     */
    @GetMapping("/sendemailcode")
    public Result<String> sendEmailCode(@RequestParam String email) {
        //生成六位随机数
        String code = RandomUtil.randomNumbers(6);
        log.info("邮箱验证码为：" + code);
        //将验证码存入redis，有效期为5分钟
        redisTemplate.opsForValue().set(emailVerifyCodeRedisPrefix + email, code, timeout, TimeUnit.MILLISECONDS);
        // 向第三方服务发送验证码
        thirdPartyFeignService.sendEmailCode(email, code);
        return Result.success("发送成功");
    }

    /**
     * 登录-通过账号或者邮箱和密码
     *
     * @param loginBodyDTO
     * @return
     */
    @PostMapping("/login")
    public Result<Map<String, Object>> login(@RequestBody LoginBodyDTO loginBodyDTO) {
        // 1. 获取传递过来的图形验证码token
        String imageToken = imageVerifyCodeRedisPrefix + loginBodyDTO.getToken();
        // 2. 从redis中获取验证码
        String text = (String) redisTemplate.opsForValue().get(imageToken);

        // 2. 对比验证码
        if (text == null) {
            throw new VerityCodeException(MessageConstant.VERITY_CODE_EXPIRED);
        } else if (loginBodyDTO.getCode().equalsIgnoreCase(text)) {
            // 查询数据库
            UserVO userVO = userService.login(loginBodyDTO.getUsernameOrEmail(), loginBodyDTO.getPassword());
            String token = getToken(userVO);
            // 封装数据并放回
            Map<String, Object> resultMap = new HashMap<>();
            userVO.setAvatar(Base64Util.convertImageToBase64(basePath + userVO.getAvatar()));
            resultMap.put(RequestMapKeyConstant.TOKEN, token);
            resultMap.put(RequestMapKeyConstant.USER, userVO);
            return Result.success(resultMap);
        } else {
            throw new VerityCodeException(MessageConstant.VERITY_CODE_UNKNOWN_ERROR);
        }
    }


    /**
     * 通过邮箱登录
     *
     * @param emailLoginBodyDTO
     * @return
     */
    @PostMapping("/loginbyemail")
    public Result<Map<String, Object>> loginByEmail(@RequestBody EmailLoginBodyDTO emailLoginBodyDTO) {
        // 1. 获取传递过来的邮箱
        String emailKey = emailVerifyCodeRedisPrefix + emailLoginBodyDTO.getEmail();
        // 2. 从redis中获取验证码
        String storedToken = (String) redisTemplate.opsForValue().get(emailKey);
        // 3. 对比验证码
        if (storedToken == null) {
            throw new VerityCodeException(MessageConstant.VERITY_CODE_EXPIRED);
        } else if (emailLoginBodyDTO.getCode().equals(storedToken)) {
            // 查询数据库
            UserVO userVO = userService.getUserInfoByEmail(emailLoginBodyDTO.getEmail());
            String token = getToken(userVO);
            // 封装数据并放回
            Map<String, Object> resultMap = new HashMap<>();
            userVO.setAvatar(Base64Util.convertImageToBase64(basePath + userVO.getAvatar()));
            resultMap.put(RequestMapKeyConstant.TOKEN, token);
            resultMap.put(RequestMapKeyConstant.USER, userVO);
            return Result.success(resultMap);
        } else {
            throw new VerityCodeException(MessageConstant.VERITY_CODE_UNKNOWN_ERROR);
        }
    }

    /**
     * 注册用户
     * @param registerDTO
     * @return
     */
    @PostMapping("/register")
    public Result<String> register(@RequestBody RegisterDTO registerDTO) {
        // 1. 获取传递过来的邮箱
        String emailKey = emailVerifyCodeRedisPrefix + registerDTO.getEmail();
        // 2. 从redis中获取验证码
        String storedToken = (String) redisTemplate.opsForValue().get(emailKey);
        // 3. 对比验证码
        if (storedToken == null) {
            throw new VerityCodeException(MessageConstant.VERITY_CODE_EXPIRED);
        } else if (registerDTO.getCode().equalsIgnoreCase(storedToken)) {
            userService.register(registerDTO);
            return Result.success();
        } else {
            throw new VerityCodeException(MessageConstant.VERITY_CODE_UNKNOWN_ERROR);
        }
    }

    @PutMapping("/password")
    public Result<String> recoverPassword(@RequestBody RecoverPasswordDTO recoverPasswordDTO) {
        // 1. 对比两次密码
        if (!recoverPasswordDTO.getPassword().equals(recoverPasswordDTO.getRePassword())) {
            throw new PasswordErrorException(MessageConstant.PASSWORD_NOT_MATCH);
        }
        // 2. 获取传递过来的邮箱
        String emailKey = emailVerifyCodeRedisPrefix + recoverPasswordDTO.getEmail();
        // 3. 从redis中获取验证码
        String storedToken = (String) redisTemplate.opsForValue().get(emailKey);
        // 4. 对比验证码
        if (storedToken == null) {
            throw new VerityCodeException(MessageConstant.VERITY_CODE_EXPIRED);
        } else if (recoverPasswordDTO.getCode().equalsIgnoreCase(storedToken)) {
            userService.recoverPassword(recoverPasswordDTO);
            return Result.success();
        } else {
            throw new VerityCodeException(MessageConstant.VERITY_CODE_UNKNOWN_ERROR);
        }
    }

    /**
     * 获取token
     *
     * @param userVO
     * @return
     */
    private String getToken(UserVO userVO) {
        // 生成jwt令牌
        Map<String, Object> claims = new HashMap<>();
        claims.put(JwtClaimsConstant.USER_ID, userVO.getId());
        String token = JwtUtil.createJWT(
                jwtUserProperties.getUserSecretKey(),
                jwtUserProperties.getUserTtl(),
                claims);
        return token;
    }
}
